package YahooFinance.network;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;

/**
 * AsyncHttpClient
 * 
 * @author rfroehlich
 * 
 */
@SuppressWarnings("deprecation")
public class AsyncHttpClient extends Thread {

	public enum HttpMethod {
		GET, POST, PUT, DELETE
	}

	public final String CONTENTYPE = "Content-Type";
	public final String CONTENTYPE_VALUE = "application/mobile";

	public static void startRequestWithDelegate(HttpMethod method,
			HashMap<String, String> optParams, String requestUrl,
			AsyncHttpDelegate delegate) {
		System.out.println("# AsyncHttpClient starting request: " + requestUrl);

		switch (method) {
		case GET:
			HttpGet reqGet = new HttpGet(requestUrl);
			AsyncHttpClient.startRequestWithDelegate(reqGet, optParams,
					delegate);
			break;

		case POST:
			HttpPost reqPost = new HttpPost(requestUrl);

			AsyncHttpClient.startRequestWithDelegate(reqPost, optParams,
					delegate);
			break;

		case PUT:
			HttpPut reqPut = new HttpPut(requestUrl);
			AsyncHttpClient.startRequestWithDelegate(reqPut, optParams,
					delegate);
			break;

		case DELETE:
			HttpDelete reqDel = new HttpDelete(requestUrl);
			AsyncHttpClient.startRequestWithDelegate(reqDel, optParams,
					delegate);
			break;

		default:
			break;
		}

	}

	public static void startRequestWithDelegate(HttpRequestBase request,
			HashMap<String, String> optParams, AsyncHttpDelegate delegate) {
		// Handler needs to be created here so that the delegate will be called
		// on this very same thread after the requests finishes
		new AsyncHttpClient(request, optParams, delegate).start();
	}

	HttpRequestBase request;
	AsyncHttpDelegate delegate;
	Thread thread;
	HashMap<String, String> optParams;

	AsyncHttpClient(HttpRequestBase _request,
			HashMap<String, String> _optParams, AsyncHttpDelegate _delegate) {
		// ??? _request.addHeader(CONTENTYPE, CONTENTYPE_VALUE);
		request = _request;
		delegate = _delegate;
		// thread = _thread;
		optParams = _optParams;
	}

	/**
	 * getHttpsClient creates http client for ignore certificates and allows all
	 * users
	 * 
	 * @return DefaultHttpClient
	 */
	private static DefaultHttpClient getHttpsClient() {
		try {

			KeyStore trustStore = KeyStore.getInstance(KeyStore
					.getDefaultType());
			trustStore.load(null, null);

			MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
			sf.setHostnameVerifier(MySSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

			HttpParams params = new BasicHttpParams();
			HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
			HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);

			SchemeRegistry registry = new SchemeRegistry();
			registry.register(new Scheme("http", PlainSocketFactory
					.getSocketFactory(), 80));
			registry.register(new Scheme("https", sf, 443));

			ClientConnectionManager ccm = new ThreadSafeClientConnManager(
					params, registry);

			return new DefaultHttpClient(ccm, params);
		} catch (Exception e) {
			return new DefaultHttpClient();
		}
	}

	@Override
	public void run() {
		try {
			DefaultHttpClient httpClient = getHttpsClient();
			httpClient.getParams().setParameter(CoreProtocolPNames.USER_AGENT,
					"Android");
			// httpClient.getParams().setParameter(
			// "http.protocol.single-cookie-header", true);
			httpClient.getParams().setParameter(ClientPNames.COOKIE_POLICY,
					CookiePolicy.BROWSER_COMPATIBILITY);

			httpClient.setRedirectHandler(new RedirectHandler());
			HttpContext myContext = new BasicHttpContext();
			HttpResponse response = httpClient.execute(request, myContext);
			StatusLine status = response.getStatusLine();

			if (status.getStatusCode() != 200) {
				throw new IOException("Invalid response from server: "
						+ status.toString());
			}

			InputStream inputStream = response.getEntity().getContent();

			// Read response into a buffered stream
			ByteArrayOutputStream content = new ByteArrayOutputStream();
			int readBytes = 0;
			byte[] sBuffer = new byte[512];
			while ((readBytes = inputStream.read(sBuffer)) != -1) {
				content.write(sBuffer, 0, readBytes);
			}

			final String dataAsString = content.toString("UTF-8");

			// execute delegate's method on the original thread (associated with

			if (optParams != null) {
				String optParam = optParams.get("optParam");
				if (optParam != null) {
					delegate.responseReceived(null, dataAsString, optParam);
				}
			} else {
				delegate.responseReceived(null, dataAsString, null);
			}

		} catch (final ClientProtocolException e) {

			String optParam = null;
			if (optParams != null) {
				if (optParams.get("optParam") != null) {
					optParam = optParams.get("optParam");
				}
				delegate.requestFailed(e, optParam);
			} else {
				delegate.requestFailed(e, "");
			}

		} catch (final IOException e) {

			String optParam = null;
			if (optParams != null) {
				if (optParams.get("optParam") != null) {
					optParam = optParams.get("optParam");
				}
				delegate.requestFailed(e, optParam);
			} else {
				delegate.requestFailed(e, "");
			}
		}
	}

	// always verify the host - dont check for certificate
	final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
		public boolean verify(String hostname, SSLSession session) {
			return true;
		}
	};

	/**
	 * Trust every server - dont check for any certificate
	 */
	private static void trustAllHosts() {
		// Create a trust manager that does not validate certificate chains
		TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
			public java.security.cert.X509Certificate[] getAcceptedIssuers() {
				return new java.security.cert.X509Certificate[] {};
			}

			public void checkClientTrusted(X509Certificate[] chain,
					String authType) throws CertificateException {
			}

			public void checkServerTrusted(X509Certificate[] chain,
					String authType) throws CertificateException {
			}
		} };

		// Install the all-trusting trust manager
		try {
			SSLContext sc = SSLContext.getInstance("TLS");
			sc.init(null, trustAllCerts, new java.security.SecureRandom());
			HttpsURLConnection
					.setDefaultSSLSocketFactory(sc.getSocketFactory());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}
